package classifier;

import java.util.ArrayList;
import java.util.List;

import learner.Pattern;

import learner.DTWCostCalculator;
import classifier.DTWModelReader;
import learner.PatternType;
import learner.PatternReader;

public class DTWClassifier {

	private DTWCostCalculator calc = new DTWCostCalculator();
	private DTWModelReader modRead = new DTWModelReader();
	private final double TOLERANCE_FACTOR = 1.1;

	public DTWClassifier(){
	}

	//test het programma voor een patroon uit de learningset
	public static void main(String[] args) {
		searchTruePositives();
		searchFalsePositives();
	}

	private static void searchFalsePositives() {
		int totalTries=0;
		int patternTypeNb =7;
		int nbFalsePositives = 0;
		for(int patternNb = 0; patternNb<10;patternNb++){
			DTWClassifier dtwC = new DTWClassifier();
			PatternReader reader= new PatternReader();
			List<Pattern> patterns =reader.givePatterns(patternTypeNb);
			Pattern pattern = patterns.get(patternNb);
			nbFalsePositives+=dtwC.classificationFalse(pattern);
			totalTries++;
		}
		System.out.println("False positives: "+ nbFalsePositives + " op " + totalTries);
	}

	//probeert elk pattern uit de learning set te kwalificeren op basis van de learning set min het pattern zelf
	//print eventuele fouten en de totaalscore uit
	private static void searchTruePositives() {
		int totalNbTruePositives =0;
		int totalTries=0;
		for(int patternTypeNb = 1; patternTypeNb<7;patternTypeNb++){
			int nbTruePositives = 0;
			for(int patternNb = 0; patternNb<10;patternNb++){
				DTWClassifier dtwC = new DTWClassifier();
				PatternReader reader= new PatternReader();
				List<Pattern> patterns =reader.givePatterns(patternTypeNb);
				Pattern pattern = patterns.get(patternNb);
				nbTruePositives+=dtwC.classificationCorrect(pattern,patternTypeNb,patternNb);
				totalTries++;
			}
			for(PatternType patType: PatternType.values()){
				if(patType.ordinal()+1==patternTypeNb){
					if(nbTruePositives!=10){
						System.out.println("True positives for " + patType.name()+ ": " + nbTruePositives);
					}
				}
			}
			totalNbTruePositives+=nbTruePositives;
		}
		System.out.println("True positives: "+ totalNbTruePositives + " op " + totalTries);
	}

	//probeert een gegeven patroon te kwalificeren door de nearest neighbour te zoeken in de training set,
	//print informatie uit over het gevonden patroon, de gevonden distance en de threshold
	public String classify(Pattern givenPattern){
		PatternReader reader = new PatternReader();
		PatternType bestFittingPattern=PatternType.values()[0];
		double smallestCost = Double.MAX_VALUE;
		for (PatternType patternType : PatternType.values()) {
			List<Pattern> patterns = new ArrayList<Pattern>();
			patterns = reader.givePatterns(patternType.ordinal()+1);
			for(Pattern pattern: patterns){
				if(patternType==PatternType.SKIPPING && !givenPattern.containsSomeSkips()){
					break;
				}
				double newCost = calc.calculateCost(pattern,givenPattern);
				System.out.println(patternType + " " + newCost);
				if(newCost<smallestCost){
					smallestCost= newCost;
					bestFittingPattern= patternType;
				}
			}
		}
		double threshold = TOLERANCE_FACTOR * modRead.giveModel(bestFittingPattern.ordinal()+1).getThreshold();
		if(smallestCost<=threshold)
			return ("DTW detected the pattern: "+ bestFittingPattern + " cost: "+ smallestCost+ " threshold is: " + threshold);
		else
			return ("DTW detected no pattern in this data!" + " Best: " + bestFittingPattern + " cost: "+ smallestCost +  " but threshold is: " + threshold);
	}

	//probeert een patroon uit de learning set te kwalificeren op de training set min zichzelf, returnt 1 indien
	//juist gekwalificeerd, returnt 0 indien niet.
	public int classificationCorrect(Pattern givenPattern, int patTypeNb, int patNb){
		PatternReader reader = new PatternReader();
		PatternType bestFittingPattern=PatternType.values()[0];
		double smallestCost = Double.MAX_VALUE;
		for (PatternType patternType : PatternType.values()) {
			List<Pattern> patterns = new ArrayList<Pattern>();
			patterns = reader.givePatterns(patternType.ordinal()+1);
			for(int i=0;i<patterns.size();i++){
				if(patternType==PatternType.SKIPPING && !givenPattern.containsSomeSkips()){
					break;
				}
				double newCost = calc.calculateCost(patterns.get(i),givenPattern);
				if(newCost<smallestCost && !(patternType.ordinal()+1==patTypeNb && i == patNb)){
					smallestCost= newCost;
					bestFittingPattern= patternType;
				}
			}
		}
		double threshold = TOLERANCE_FACTOR * modRead.giveModel(bestFittingPattern.ordinal()+1).getThreshold();
		if(smallestCost<=threshold){
			if(bestFittingPattern.ordinal()+1==patTypeNb){
				return 1;
			}
			else{
				return 0;
			}
		}
		else{
			System.out.println("DTW detected no pattern in this data!" + " Best: " + bestFittingPattern + " cost: "+ smallestCost +  " but threshold is: " + threshold);
			return 0;
		}
	}
	
	//probeert een patroon dat op niets zou mogen lijken te kwalificeren, als het toch lukt print het dit uit en
	//returnt het 1, anders returnt het 0
	private int classificationFalse(Pattern givenPattern) {
		PatternReader reader = new PatternReader();
		PatternType bestFittingPattern=PatternType.values()[0];
		double smallestCost = Double.MAX_VALUE;
		for (PatternType patternType : PatternType.values()) {
			List<Pattern> patterns = new ArrayList<Pattern>();
			patterns = reader.givePatterns(patternType.ordinal()+1);
			for(int i=0;i<patterns.size();i++){
				if(patternType==PatternType.SKIPPING && !givenPattern.containsSomeSkips()){
					break;
				}
				double newCost = calc.calculateCost(patterns.get(i),givenPattern);
				if(newCost<smallestCost){
					smallestCost= newCost;
					bestFittingPattern= patternType;
				}
			}
		}
		double threshold = TOLERANCE_FACTOR * modRead.giveModel(bestFittingPattern.ordinal()+1).getThreshold();
		if(smallestCost<=threshold){
			System.out.println("Falsely classificated as: " + bestFittingPattern + " cost: "+ smallestCost + ", threshold: "+ threshold);
			return 1;
		}
		else{
			return 0;
		}
	}

}
